// Copyright (c) 2011 Blue Onion Software, All rights reserved
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Web.Hosting;
using System.Xml.Serialization;

namespace BlueOnionSoftware.Bloget
{
    internal static class ImportBlog
    {
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        internal static void Import(Blog blog, Stream stream)
        {
            Throw.IfNull(blog, "blog");
            Throw.IfNull(stream, "stream");

            var serializer = new XmlSerializer(typeof (blogType));
            var blogML = (blogType) serializer.Deserialize(stream);
            blog = new Blog(blog); // protect readers

            blog.Title = blogML.title.Text[0];
            blog.Description = blogML.subtitle.Text[0];
            blog.LicenseAgreement = ExtendedBoolProperty(blogML, "accepted_license", blog.LicenseAgreement);
            //blog.FirstName = ExtendedStringProperty(blogML, "author_first_name", blog.FirstName);
            //blog.LastName = ExtendedStringProperty(blogML, "author_last_name", blog.LastName);
            blog.Language = ExtendedStringProperty(blogML, "language", blog.Language);
            blog.Copyright = ExtendedStringProperty(blogML, "copyright", blog.Copyright);
            blog.Webmaster = ExtendedStringProperty(blogML, "webmaster", blog.Webmaster);
            //blog.AuthorEmail = ExtendedStringProperty(blogML, "author_email", blog.AuthorEmail);
            blog.RssChannelImage = ExtendedStringProperty(blogML, "rss_image", blog.RssChannelImage);
            blog.RssFooter = ExtendedStringProperty(blogML, "rss_footer", blog.RssFooter);
            blog.RssEntriesPerFeed = ExtendedIntProperty(blogML, "rss_entries_per_feed", blog.RssEntriesPerFeed);
            blog.PostsPerPage = ExtendedIntProperty(blogML, "posts_per_page", blog.PostsPerPage);
            blog.AllowComments = ExtendedBoolProperty(blogML, "allow_comments", blog.AllowComments);
            blog.MaximumCommentsPerPost = ExtendedIntProperty(blogML, "max_comments_per_post", blog.MaximumCommentsPerPost);
            blog.ExpireComments = ExtendedIntProperty(blogML, "expire_comments", blog.ExpireComments);
            blog.EmailComments = ExtendedBoolProperty(blogML, "email_comments", blog.EmailComments);
            blog.EmailPingbacks = ExtendedBoolProperty(blogML, "email_pingbacks", blog.EmailPingbacks);
            blog.EmailTo = ExtendedStringProperty(blogML, "email_to", blog.EmailTo);
            blog.ImageFolder = ExtendedStringProperty(blogML, "image_folder", blog.ImageFolder);
            blog.ImageUrl = ExtendedStringProperty(blogML, "image_url", blog.ImageUrl);
            blog.EnableMetaWeblogApi = ExtendedBoolProperty(blogML, "enable_megaweblogapi", blog.EnableMetaWeblogApi);
            blog.EnableRealSimpleDiscovery = ExtendedBoolProperty(blogML, "enable_rsd", blog.EnableRealSimpleDiscovery);
            blog.EnableAutoPingBack = ExtendedBoolProperty(blogML, "enable_auto_pingback", blog.EnableAutoPingBack);
            blog.EnablePingBackService = ExtendedBoolProperty(blogML, "enable_pingback_service", blog.EnablePingBackService);
            blog.ExpirePingBacks = ExtendedIntProperty(blogML, "expire_pingbacks", blog.ExpirePingBacks);
            blog.MaximumPingsBacksPerPost = ExtendedIntProperty(blogML, "maximum_pingbacks_per_post", blog.MaximumPingsBacksPerPost);

            // Need a map between bloget category ids and incoming category ids
            var categoryMap = new Dictionary<string, Category>(blog.Categories.Count);

            if (blogML.categories != null)
            {
                foreach (var category in blogML.categories)
                {
                    if (category.approved)
                    {
                        var blogetCategory = blog.Categories.CreateCategory(category.title.Text[0]);
                        categoryMap.Add(category.id, blogetCategory);
                    }
                }
            }

            foreach (var ml_post in blogML.posts)
            {
                var id = Guid.Empty;
                Post post;
                Content content;

                try
                {
                    if (ml_post.id.Length >= 36)
                    {
                        id = new Guid(ml_post.id);
                    }

                    if (blog.Posts.Contains(id) == false)
                    {
                        post = new Post();
                        content = new Content();

                        if (id != Guid.Empty)
                        {
                            post.Id = id;
                        }

                        post.Created = ml_post.datecreated;
                        blog.Posts.InsertByDate(post);
                    }

                    else
                    {
                        post = blog.Posts[id];
                        content = new Content(blog.LoadContent(id)); // protect readers
                    }
                }

                catch (Exception)
                {
                    post = new Post();
                    content = new Content();

                    if (id != Guid.Empty)
                    {
                        post.Id = id;
                    }

                    post.Created = ml_post.datecreated;
                    blog.Posts.InsertByDate(post);
                }

                post.Title = ml_post.title.Text[0];
                post.Draft = !ml_post.approved;
                post.EnableComments = blog.AllowComments;
                content.Text = ml_post.content.Value;

                if (ml_post.categories != null)
                {
                    foreach (var categoryReference in ml_post.categories)
                    {
                        post.AddCategory(categoryMap[categoryReference.@ref]);
                    }
                }

                if (ml_post.comments != null)
                {
                    foreach (var externalComment in ml_post.comments)
                    {
                        var commentId = Guid.Empty;
                        Comment comment;

                        try
                        {
                            if (externalComment.id.Length >= 36)
                            {
                                commentId = new Guid(externalComment.id);
                            }

                            if (content.Comments.Contains(commentId) == false)
                            {
                                comment = new Comment();

                                if (commentId != Guid.Empty)
                                {
                                    comment.Id = commentId;
                                }

                                content.Comments.Add(comment);
                            }

                            else
                            {
                                comment = content.Comments[commentId];
                            }
                        }

                        catch (Exception)
                        {
                            comment = new Comment();

                            if (commentId != Guid.Empty)
                            {
                                comment.Id = commentId;
                            }

                            content.Comments.Add(comment);
                        }

                        comment.Author = externalComment.username ?? string.Empty;
                        comment.Email = externalComment.useremail ?? string.Empty;

                        comment.Title = (externalComment.title != null)
                            ? externalComment.title.Text[0] ?? string.Empty
                            : string.Empty;

                        comment.Text = externalComment.content.Value;
                        comment.Date = externalComment.datecreated;
                    }
                }

                if (ml_post.attachments != null && string.IsNullOrEmpty(blog.ImageFolder) == false)
                {
                    var imageFolder = HostingEnvironment.MapPath(blog.ImageFolder);

                    if (string.IsNullOrEmpty(imageFolder) == false)
                    {
                        if (Directory.Exists(imageFolder) == false)
                        {
                            Directory.CreateDirectory(imageFolder);
                        }

                        foreach (var attachment in ml_post.attachments)
                        {
                            if (attachment.embedded)
                            {
                                using (Stream file = FileUtilities.OpenCreate(imageFolder + attachment.url))
                                {
                                    var bytes = Convert.FromBase64String(attachment.Value);
                                    file.Write(bytes, 0, bytes.Length);
                                }
                            }
                        }
                    }
                }

                if (ml_post.trackbacks != null)
                {
                    foreach (var trackback in ml_post.trackbacks)
                    {
                        var reference = new Reference();
                        reference.Link = trackback.url;
                        content.References.Add(reference);
                    }
                }

                blog.SaveContent(post.Id, content);
            }

            blog.Save();
        }

        private static string ExtendedStringProperty(blogType blogML, string property, string defaultValue)
        {
            return FindProperty(blogML, property) ?? defaultValue;
        }

        private static bool ExtendedBoolProperty(blogType blogML, string property, bool defaultValue)
        {
            bool value;
            return (bool.TryParse(FindProperty(blogML, property), out value)) ? value : defaultValue;
        }

        private static int ExtendedIntProperty(blogType blogML, string property, int defaultValue)
        {
            int value;

            if (int.TryParse(FindProperty(blogML, property),
                             NumberStyles.Integer,
                             CultureInfo.InvariantCulture,
                             out value) == false)
            {
                value = defaultValue;
            }

            return value;
        }

        private static string FindProperty(blogType blogML, string property)
        {
            if (blogML.extendedproperties != null)
            {
                foreach (var extendedProperty in blogML.extendedproperties)
                {
                    if (extendedProperty.name == property)
                    {
                        return extendedProperty.value;
                    }
                }
            }

            return null;
        }
    }
}